20. 数据加解密
📝 模块更新日志 新特性*
+ 新增 `KSortEncryption` 生成数据签名(`API` 签名算法) 4\.9\.5\.10 ⏱️2024\.09\.19 [2585d24](https://gitee.com/dotnetchina/Furion/commit/2585d24a25c5334ca13d6541ee4ad3afc8cef96f)
+ 新增 `PBKDF2` 加密和比较功能的静态类和字符串拓展支持 4\.9\.3\.7 ⏱️2024\.05\.21 [0d645d2](https://gitee.com/dotnetchina/Furion/commit/0d645d23422e6d031bd0d6a0c47e1c10e1bd68d7)
+ 新增 `SHA1` 加密和比较功能的静态类和字符串拓展支持 4\.9\.2\.41 ⏱️2024\.05\.08 [@superbisu](https://gitee.com/superbisu) [!879](https://gitee.com/dotnetchina/Furion/pulls/879) [f592757](https://gitee.com/dotnetchina/Furion/commit/f592757132f34a6e7d7f3da5ebe9d950e0af91c2)
+ 新增 `AES` 加解密支持向量 `IV`、模式 `Mode` 和填充 `Padding` 配置 4\.9\.2\.18 ⏱️2024\.04\.15 [d549bba](https://gitee.com/dotnetchina/Furion/commit/d549bba917114a57441cd3fc80795340131f3dcf)
+ 新增 `AES` 支持对文件(含超大文件)进行加解密 4\.8\.8\.11 ⏱️2023\.05\.05 [1d2265b](https://gitee.com/dotnetchina/Furion/commit/1d2265be04cfd7c6c2b9db932a77ebd620ef6054)
+ 新增 `RSA` 支持对超长字符(超 `245` 位)进行分段加解密 4\.8\.8\.2 ⏱️2023\.04\.19 [!788](https://gitee.com/dotnetchina/Furion/pulls/788) 感谢 [@YaChengMu](https://gitee.com/YaChengMu)
+ 新增 `byte[]` 类型 `MD5` 加密/比较重载方法 4\.8\.6\.3 ⏱️2023\.02\.15 [\#I6F1NT](https://gitee.com/dotnetchina/Furion/issues/I6F1NT)
-
突破性变化
- 调整
DES加解密相关类和方法命名:DESCEncryption->DESEncryption,ToDESCEncrypt->ToDESEncrypt,ToDESCDecrypt->ToDESDecrypt4.9.2.41 ⏱️2024.05.08 a46f129
- 调整
20.1 数据加解密
由于现在的互联网越具发达,数据成为了我们生活的一部分,当然也带来了很多数据安全性的问题,比如用户密码明文存储,用户信息明文存在在浏览器 cookies 中等等不安全操作。
所以,对数据的加解密是系统开发必要的环节。
20.2 内置加密算法
MD5加密/对比DES加解密AES加解密JWT加解密PBKDF2加密/对比(推荐密码加密使用这个)RSA加解密SHA1加密/对比KSortAPI数据签名(Furion 4.9.5.10+ 支持)
20.3 加解密使用
20.3.1 MD5 加密
// 测试 MD5 加密,比较
var md5Hash = MD5Encryption.Encrypt("百小僧"); // 加密
var isEqual = MD5Encryption.Compare("百小僧", md5Hash); // 比较
return (md5Hash, isEqual);
// 输出大写 MD5 加密
var md5Hash = MD5Encryption.Encrypt("百小僧", true);
// 输出 16位 MD5 加密,Furion 4.2.6+ 版本
var md5Hash16 = MD5Encryption.Encrypt("百小僧", is16: true);
// Furion 4.8.6.3+ 版本支持 byte[] 类型,如获取文件 MD5 Hash
var bytes = File.ReadAllBytes("image.png");
var md5Hash = MD5Encryption.Encrypt(bytes); // 加密
var isEqual = MD5Encryption.Compare(bytes, md5Hash); // 比较
20.3.2 DES 加解密
// 测试 DES 加解密
var desHash = DESEncryption.Encrypt("百小僧", "Furion"); // 加密
var str = DESEncryption.Decrypt(desHash, "Furion"); // 解密
return (desHash, str);
20.3.3 AES 加解密
// 测试 AES 加解密
var key = Guid.NewGuid().ToString("N"); // 密钥,长度必须为24位或32位
var aesHash = AESEncryption.Encrypt("百小僧", key); // 加密
var str2 = AESEncryption.Decrypt(aesHash, key); // 解密
return (aesHash, str2);
// Furion 4.9.2.18+ 支持向量 IV、模式 Mode 和填充 Padding 配置
// 要加密的明文
var plainText = "一个应用程序框架,您可以将它集成到任何 .NET/C# 应用程序中。";
// 密钥(必须为 16、24 或 32 字节,这里使用 32 字节)
var key = "bda66540c463dfdbe70666019f89e554"; // furion 小写 32 位 MD5 加密字符串
// 自定义偏移量(16 字节),如果不指定,则使用默认值
byte[] customIV = { 0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x70, 0x81, 0x92, 0xa3, 0xb4, 0xc5, 0xd6, 0xe7, 0xf8, 0x09 };
// 使用 CBC 模式和 PKCS7 填充进行加密
var encryptedText = AESEncryption.Encrypt(plainText, key
, customIV // 向量
, CipherMode.CBC // 模式
, PaddingMode.PKCS7); // 填充
Console.WriteLine("加密后文本: " + encryptedText);
// 使用相同的密钥、偏移量、模式和填充进行解密
var decryptedText = AESEncryption.Decrypt(encryptedText, key
, customIV // 向量
, CipherMode.CBC // 模式
, PaddingMode.PKCS7); // 填充
Console.WriteLine("解密后文本: " + decryptedText);
版本说明以下内容仅限 Furion 4.8.8.11 + 版本使用。
- 对文件(含超大文件)进行加解密
// 加密
var originBytes = File.ReadAllBytes("xxx.rar"); // 读取源文件内容
var encryptBytes = AESEncryption.Encrypt(originBytes, "123456");
// 可以通过 encryptBytes.CopyToSave("xxx.加密.rar"); 保存到磁盘
// 解密
var encryptBytes = File.ReadAllBytes("xxx.加密.rar"); // 读取加密文件内容
var originBytes = AESEncryption.Decrypt(encryptBytes, "123456");
// 可以通过 originBytes.CopyToSave("xxx.真实.rar"); 保存到磁盘
// Furion 4.9.2.18+ 支持向量 IV、模式 Mode 和填充 Padding 配置
20.3.4 JWT 加解密
var token = JWTEncryption.Encrypt(new Dictionary<string, object>() // 加密
{
{ "UserId", user.Id },
{ "Account",user.Account }
});
var tokenData = JWTEncryption.ReadJwtToken("你的token"); // 解密
var (isValid, tokenData, validationResult) = JWTEncryption.Validate("你的token"); // 验证token有效期
特别注意JWTEncryption 加解密并未包含在 Furion 框架中,需要安装 Furion 框架提供的 Furion.Extras.Authentication.JwtBearer 拓展包。
20.3.5 PBKDF2 加密
版本说明以下内容仅限 Furion 4.9.3.7+ 版本使用。
// 测试 PBKDF2 加密,比较
var pbkdf2Hash = PBKDF2Encryption.Encrypt("百小僧"); // 加密
var isEqual = PBKDF2Encryption.Compare("百小僧", pbkdf2Hash); // 比较
return (pbkdf2Hash, isEqual);
20.3.6 RSA 加密
// 测试 RSA 加密
var (publicKey, privateKey) = RSAEncryption.GenerateSecretKey(2048); //生成 RSA 秘钥 秘钥大小必须为 2048 到 16384,并且是 8 的倍数
var basestring = RSAEncryption.Encrypt("百小僧", publicKey); // 加密
var str2 = RSAEncryption.Decrypt(basestring, privateKey); // 解密
return (basestring, str2);
关于 RSA 签名和校验Furion 框架底层不内置 RSA 签名和校验功能,如需添加该功能可查阅开发者提交的代码:查看 RSA 签名和校验
20.3.7 SHA1 加密
版本说明以下内容仅限 Furion 4.9.2.41 + 版本使用。
// 测试 SHA1 加密,比较
var sha1Hash = SHA1Encryption.Encrypt("百小僧"); // 加密
var isEqual = SHA1Encryption.Compare("百小僧", sha1Hash); // 比较
// 输出大写 SHA1 加密
var sha1Hash2 = SHA1Encryption.Encrypt("百小僧", true);
// byte[] 类型,如获取文件 SHA1 Hash
var bytes = File.ReadAllBytes("image.png");
var sha1Hash3 = SHA1Encryption.Encrypt(bytes); // 加密
var isEqual2 = SHA1Encryption.Compare(bytes, sha1Hash3); // 比较
20.3.8 KSort 数据签名(API 签名)
版本说明以下内容仅限 Furion 4.9.5.10 + 版本使用。
相关说明:https://gitee.com/dotnetchina/Furion/commit/2585d24a25c5334ca13d6541ee4ad3afc8cef96f
- 使用
C#为API数据签名
// 应用标识/密钥(可在自己系统中实现生成)
var appId = "ca36cb2858ce3517df772ec34ce92f21";
var appKey = "95e4a4f651c2d62679c4c150f2e39f4a";
// 本次提交命令(标识符)
var command = "add.user";
// 序列化需要签名的数据
var data = JsonSerializer.Serialize(new { id = 1, name = "Furion" });
// 得到签名后的数据
KSortSignature sData = KSortEncryption.Encrypt(appId, appKey, command, data);
sData 的格式如下:
{
"app_id": "ca36cb2858ce3517df772ec34ce92f21",
"app_key": "95e4a4f651c2d62679c4c150f2e39f4a",
"command": "add.user",
"data": "{\"id\":1,\"name\":\"Furion\"}",
"timestamp": 1726695114461,
"signature": "f6ead3f509c952249f3046ebc4e6e149"
}
- 使用
C#验证数据签名
// 带签名的数据
var dataString = """{
"app_id": "ca36cb2858ce3517df772ec34ce92f21",
"app_key": "95e4a4f651c2d62679c4c150f2e39f4a",
"command": "add.user",
"data": "{\"id\":1,\"name\":\"Furion\"}",
"timestamp": 1726695114461,
"signature": "f6ead3f509c952249f3046ebc4e6e149"
}""";
// 验证数据是否被篡改
bool result = KSortEncryption.Compare(dataString);
另外,KSortEncryption.Compare 支持重载,支持传入 JSON 字符串或 KSortSignature 对象。
-
使用
TypeScript/JavaScript为API数据签名 -
客户端需安装
crypto-js:
npm install crypto-js
- 拷贝
ksort签名算法代码:https://gitee.com/dotnetchina/Furion/blob/v4/clients/data-signature.ts - 生成
API数据签名
// 对数据进行签名
var sData = signatureByKSort(
"ca36cb2858ce3517df772ec34ce92f21",
"95e4a4f651c2d62679c4c150f2e39f4a",
"add.user",
{ id: 1, name: "furion" }
);
// 提交数据
fetch("http://furion.net", sData)
.then(res=>res.json())
.then(data=>{});
-
最佳实践
-
客户端应该从服务器获取
appId和appKey,切勿明文写在客户端代码中。 - 后端接收数据的参数类型统一采用
KSortSignature类,如:
[HttpPost]
public async Task AddUser([Required]KSortSignature sData)
{
// 验证签名
if(!KSortEncryption.Compare(sData))
{
throw new Exception("非法数据");
}
// 反序列化得到原始数据
var data = JsonSerializer.Deserialize<原始数据类型>(sData.data);
// ...
}
- 也可以实现中间件统一验证。
20.4 字符串拓展方式
Furion 框架也提供了字符串拓展方式进行 MD5加密、AES/DES加解密、RSA加解密。
using Furion.DataEncryption.Extensions;
// MD5 加密
var s = "Furion".ToMD5Encrypt();
var b = "Furion".ToMD5Compare(s); // 比较
// Furion 4.8.6.3+ 支持 bytes
var b = bytes.ToMD5Encrypt();
var z = bytes2.ToMD5Compare(bytes);
// AES加解密
var s = "Furion".ToAESEncrypt("sfdsfdsfdsfdsfdsfdsfdsfdsfdfdsfdsfdfdfdfd");
var str = s.ToAESDecrypt("sfdsfdsfdsfdsfdsfdsfdsfdsfdfdsfdsfdfdfdfd");
// DES 加解密
var s = "Furion".ToDESEncrypt("sfdsfdsfdsfdsfdsfdsfdsfdsfdfdsfdsfdfdfdfd");
var str = s.ToDESDecrypt("sfdsfdsfdsfdsfdsfdsfdsfdsfdfdsfdsfdfdfdfd");
// PBKDF2 加密(`Furion v2.12 +` 版本已移除!!!!!!!!)
var s = "Furion".ToPBKDF2Encrypt();
var b = "Furion".ToPBKDF2Compare(s); // 比较
// RSA 加解密
var (publicKey, privateKey) = RSAEncryption.GenerateSecretKey(2048); //生成 RSA 秘钥 秘钥大小必须为 2048 到 16384,并且是 8 的倍数
var s= "Furion".ToRSAEncrpyt(publicKey); // 加密
var str=s.ToRSADecrypt(privateKey); // 解密
// SHA1 加密,Furion 4.9.2.41+ 支持
var s = "Furion".ToSHA1Encrypt();
var b = "Furion".ToSHA1Compare(s); // 比较
// bytes
var b = bytes.ToSHA1Encrypt();
var z = bytes2.ToSHA1Compare(bytes);
20.5 SM2、SM3,SM4 国密
Furion 框架未内置国密算法 SM2-4,但是已有开发者贡献实现并开源,可查阅 Gitee 仓库,感谢 QQ(373696184)形影相印²º²² 贡献
var data = "{\"lx\":\"1\",\"wxid\":\"\",\"ehealth_code_id\":\"68A018036186B717CC1B051C10996F4EEE805F5F81EB1594C9EB43592545F7F6\",\"ehealth_code\":\"68A018036186B717CC1B051C10996F4EEE805F5F81EB1594C9EB43592545F7F6\",\"xm\":\"测试\"}";
// SM2
var b = SM2Utils.加密("123");
var b1 = SM2Utils.解密(b);
// SM3 一般用于数字签名
var sM3Utils = new SM3Utils();
sM3Utils.secretKey = "ASAFSDFDSGSDFSDFSDFSFSF";
var token= sM3Utils.加密("123");
// SM4
var sM4Utils = new SM4Utils();
sM4Utils.secretKey = "BDBDBDBDBDBDBDBDBDBDBDBDBDBDBD";
var a = sM4Utils.加密(data);
var a1 = sM4Utils.解密(a);
20.6 反馈与建议
与我们交流给 Furion 提 Issue。